#import "SFUDirectorySource.h"

//  Reference to ElementParser (third party source) used in this class.
//
//  Created by Lee Buck on 4/21/09.
//  Copyright 2009 Blue Bright Ventures. All rights reserved.
//
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.

//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.

//	Commercial licences without many of the obligations of GPL 
//	are available for a nominal fee at sales@touchtankapps.com.

//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.

/**
 * An SFUDirectorySource is an object representing the SFU business
 * faculty & staff directory.
 *
 * Refers to the website "http://business.sfu.ca/directory.php".
 * Conforms to the WebParsable protocol.
 *
 * Code used within this class and the supporting API was written by Lee Buck.
 * The supporting API can be found in ./ElementParser folder.
 *
 * @author      James Lin
 * @author      Lee Buck
 * @version     1.0.4
 * @since       1.0.4
 * @see         SFUWebSource
 */
@implementation SFUDirectorySource

@synthesize request;
@synthesize connection;

/**
 * Initialize the SFU directory source with a URL.
 *
 * The connection is asynchronous.
 *
 * @param theDelegate  the delegate to handle connection events
 * @return             the initialized SFUDirectorySource
 * @see	               SFUConnectionDelegate
 */
- (id) initWithDelegate:(id<SFUConnectionDelegate>)theDelegate {
	if (self = [super initWithURLString:@"http://business.sfu.ca/directory.php" 
							   delegate:theDelegate]) {
		self.request = [NSMutableURLRequest requestWithURL:self.sourceURL 
										  cachePolicy:NSURLRequestUseProtocolCachePolicy 
									  timeoutInterval:CONN_TIMEOUT_INTERVAL];
		[request setHTTPMethod:@"POST"];
		NSString *postString = @"EmployeeType=ALL";
		[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
		
	}
	return self;
}

/**
 * Initialize the load request to SFU directory and retrieve source code.
 *
 * @return  whether it was successful
 */
- (BOOL)downloadSourceCode {
	self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
	if (connection) {
		receivedData = [[NSMutableData data] retain];
	} else {
		NSLog(@"Connection error: %@", connection);
	}
	
	return ([self.sourceCode length] > 0);
}

/**
 * Parse the name and about URL of the contact.
 */
- (void)parseNameFromElement:(Element*)name intoContact:(SFUContact*)contact {
	// Parse out the about URL
	NSString* aboutURL = [[name selectElement:@"a"] attribute:@"href"];
	aboutURL = !aboutURL ? @"" : [NSString stringWithFormat:@"%@%@", 
								  @"http://business.sfu.ca", aboutURL];
	
	// Parse out the first and last name
	NSString* fullName = [[name selectElement:@"b"] contentsText];
	fullName = [fullName stringByReplacingOccurrencesOfString:@"  " withString:@" "];
	NSArray* names = [fullName componentsSeparatedByString:@" "];
	NSString* lastName = [names objectAtIndex:1];
	NSString* firstName = [names objectAtIndex:0];
	
	// Parse out the job title
	NSString* jobTitle = [[name selectElement:@"i > font"] contentsText];
	jobTitle = !jobTitle ? @"" : jobTitle;
	
	contact.lastName = lastName;
	contact.firstName = firstName;
	contact.jobTitle = jobTitle;
	contact.aboutURL = aboutURL;
}

/**
 * Parse the phone numbers of the contact.
 */
- (void)parsePhoneFromElement:(Element*)phone intoContact:(SFUContact*)contact {
	// Create a container for the phone numbers
	NSMutableDictionary* phoneDict = [[NSMutableDictionary alloc] initWithCapacity:0];
	
	NSArray* tempElements = [phone selectElements:@"font"];
	
	// Check for multiplicity of phone numbers
	if ([tempElements count] == 1) {
		if (![[[tempElements objectAtIndex:0] contentsText] isEqualToString:@"n/a"]) {
			[phoneDict setObject:[[tempElements objectAtIndex:0] contentsText] forKey:@"Work"];
		}
	} else if ([tempElements count] > 1) {
		for (int i = 0; i <[tempElements count]; i += 2) {
			NSString* location = [[[[tempElements objectAtIndex:i] selectElement:@"b"] contentsText] 
								  stringByReplacingOccurrencesOfString:@":" 
															withString:@""];
			NSString* phoneNum = [[tempElements objectAtIndex:i+1] contentsText];
			
			// Add to the phone dictionary
			if (![phoneNum isEqualToString:@"n/a"]) {
				[phoneDict setObject:phoneNum forKey:location];
			}
		}
	}
	
	contact.phoneDict = phoneDict;
	[phoneDict release];
}

/**
 * Parse the room numbers of the contact.
 */
- (void)parseRoomFromElement:(Element*)room intoContact:(SFUContact*)contact {
	// Create a container for the room numbers
	NSMutableDictionary* roomDict = [[NSMutableDictionary alloc] initWithCapacity:0];
	
	NSArray* tempElements = [room selectElements:@"font"];
	
	// Check for multiplicity of room numbers
	if ([tempElements count] == 1) {
		if (![[[tempElements objectAtIndex:0] contentsText] isEqualToString:@"n/a"]) {
			[roomDict setObject:[[tempElements objectAtIndex:0] contentsText] forKey:@"Office"];
		}
	} else if ([tempElements count] > 1) {
		for (int i = 0; i <[tempElements count]; i += 2) {
			NSString* location = [[[[tempElements objectAtIndex:i] selectElement:@"b"] contentsText] 
								  stringByReplacingOccurrencesOfString:@":" 
								  withString:@""];
			NSString* roomNum = [[tempElements objectAtIndex:i+1] contentsText];
			
			// Add to the room dictionary
			if (![roomNum isEqualToString:@"n/a"]) {
				[roomDict setObject:roomNum forKey:location];
			}
			
			location = nil;
			roomNum = nil;
		}
	}
	
	contact.roomDict = roomDict;
	[roomDict release];
}

/**
 * Parse the email of the contact.
 */
- (void)parseEmailFromElement:(Element*)email intoContact:(SFUContact*)contact {
	// Tidy up the Javascript syntax surrounding the email string
	NSString* emailString = [[email selectElement:@"script"] contentsSource];
	emailString = [emailString stringByReplacingOccurrencesOfString:@"\"" withString:@""];
	emailString = [emailString stringByReplacingOccurrencesOfString:@"\t" withString:@""];
	emailString = [emailString stringByReplacingOccurrencesOfString:@");" withString:@""];
	emailString = [emailString stringByReplacingOccurrencesOfString:@"\r\n" withString:@""];
	emailString = [emailString stringByReplacingOccurrencesOfString:@" " withString:@""];
	
	NSArray* emailComponents = [emailString componentsSeparatedByString:@"document.write("];
	
	// Ensure the parsed email is valid
	NSString* parsedEmail = [NSString stringWithFormat:@"%@%@%@", [emailComponents objectAtIndex:2], 
																  [emailComponents objectAtIndex:3],
																  [emailComponents objectAtIndex:4]];
	parsedEmail = !parsedEmail ? @"" : parsedEmail;
	contact.email = parsedEmail;
}

/**
 * Parse the events from the SFU events calendar website.
 *
 * @precondition  the source code has been completely loaded
 * @return        an array of the parsed SFU events
 * @see	          SFUWebSource#parseElements
 */
- (NSArray*)parseElements {
	// Create a container for the parsed contacts
	NSMutableArray* parsedContacts = [[NSMutableArray alloc] init];
	
	// Begin parse
	document = [Element parseHTML: self.sourceCode];
	
	// Begin parse: select start tags of each directory contact
	tempContacts = [document selectElements:@"td.linelist"];
	
	// Process each contact
	for (int i = 0; i < [tempContacts count]; i += 4) {
		SFUContact* parsedContact = [[SFUContact alloc] init];
		
		// Delegate the four columns of data to parse to the four helper methods
		if ([tempContacts objectAtIndex:i] && [tempContacts objectAtIndex:i+1] && 
			[tempContacts objectAtIndex:i+2] && [tempContacts objectAtIndex:i+3]) {
			//NSLog(@"%d of %d", i/4, [tempContacts count]/4);
			[self parseNameFromElement:[tempContacts objectAtIndex:i] 
						   intoContact:parsedContact];
			[self parsePhoneFromElement:[tempContacts objectAtIndex:i+1] 
							intoContact:parsedContact];
			[self parseRoomFromElement:[tempContacts objectAtIndex:i+2] 
						   intoContact:parsedContact];
			[self parseEmailFromElement:[tempContacts objectAtIndex:i+3] 
							intoContact:parsedContact];
		}
		
		// Print out log for verification testing
		/*
		NSLog(@"---------------------------------");
		NSLog(@"Name: %@ %@", parsedContact.firstName, parsedContact.lastName);
		NSLog(@"Job title: %@", parsedContact.jobTitle);
		for (NSString* phone in [parsedContact.phoneDict allKeys]) {
			NSLog(@"Phone: %@ (%@)", [parsedContact.phoneDict objectForKey:phone], phone);
		}
		for (NSString* room in [parsedContact.roomDict allKeys]) {
			NSLog(@"Room: %@ (%@)", [parsedContact.roomDict objectForKey:room], room);
		}
		NSLog(@"Email: %@", parsedContact.email);
		NSLog(@"About URL: %@", parsedContact.aboutURL);
		*/
		
		// Add contact container to the temporary collection
		[parsedContacts addObject:parsedContact];
		[parsedContact release];
	}
	
	return parsedContacts;
}


/**
 * Handle NSURLConnection response received event.
 */
- (void)connection:(NSURLConnection *)theConnection didReceiveResponse:(NSURLResponse *)response {
	[receivedData setLength:0];
}

/**
 * Handle NSURLConnection data received event.
 */
- (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)data {
	[receivedData appendData:data];
}

/**
 * Handle NSURLConnection failed with error event.
 */
- (void)connection:(NSURLConnection *)theConnection didFailWithError:(NSError *)error {
    [self.delegate connectionDidFail:@"Connection failed."];
}

/**
 * Handle NSURLConnection finished loading event.
 */
- (void)connectionDidFinishLoading:(NSURLConnection *)theConnection {
	self.sourceCode = [[NSString alloc] initWithData:receivedData 
											encoding:NSASCIIStringEncoding];
	[self.delegate connectionDidFinish:@"Connection finished." 
							   results:nil];
}

/**
 * Deallocate the SFU directory source.
 */
- (void)dealloc {
	[request release];
	request = nil;
	[connection release];
	connection = nil;
	[receivedData release];
	receivedData = nil;
	tempContacts = nil;
	document = nil;
	[super dealloc];
}

@end
